package sv.com.renacempleo.web.util;

import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.EntityMode;
import org.hibernate.SessionFactory;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.proxy.HibernateProxy;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.core.context.SecurityContextHolder;

import sv.com.renacempleo.annotation.WebProperty;
import sv.com.renacempleo.dto.sgd.SgdUsuario;
import sv.com.renacempleo.exception.WebException;
import sv.com.renacempleo.web.security.CustomUser;

public class WebUtilidades extends WebUtilidadesConstants{

	private static final long serialVersionUID = -9105983026436275095L;
	public static Map<String, Object> applicationDaos = null;
	public static Map<String, Object> applicationServices = null;
	public static List<XmlMarshalledObjectQuery> webQueries = null;
	public static List<XmlMarshalledObjectQuery> webDocumentsStructres = null;
	
	
	public static void preProcessBeforeSaveOrUpdate(Object object, SessionFactory session) {
		try {
			if(object instanceof HibernateProxy)
				object = ((HibernateProxy)object).getHibernateLazyInitializer().getImplementation();
			Field[] fields = object.getClass().getDeclaredFields();
			Class<?> clazz = null;
			Object value = null;
			Serializable id = null;
			ClassMetadata classMetadata = null;
			WebProperty webProperty = null;
			ManyToOne manyToOne = null;
			Column column = null;
			Id idColumn = null;
			for (Field field : fields) {
				webProperty = field.getAnnotation(WebProperty.class);
				manyToOne = field.getAnnotation(ManyToOne.class);
				column = field.getAnnotation(Column.class);
				idColumn = field.getAnnotation(Id.class);
				field.setAccessible(true);
				value = field.get(object);
				if (value == null) continue;
				if (manyToOne != null) {
					classMetadata = session.getClassMetadata(value.getClass());
					if (classMetadata != null) {
						id = classMetadata.getIdentifier(value, EntityMode.POJO);
						if (id == null) field.set(object, null);
					}
				}
				if(idColumn != null) {
					if(value != null && value.equals(0L)){
						field.set(object, null);
					}
				}
				if (webProperty != null && webProperty.skipUppercase()) continue;
				clazz = field.getType();
				if (String.class.equals(clazz)) {
					field.set(object, StringUtils.upperCase((String) value));
				}
				if (column != null) {
					int length = column.length();
					if (length == 1) {
						if ("true".equals(value)) field.set(object, "1");
						else if ("false".equals(value)) field.set(object, "0");
					}
				}
			}
		}
		catch (Exception e) {
//			logger.error("Error ejecutando pre-procesos antes de guardar objeto: " + e.getMessage(), e);
		}
	}
	
	public static Object getApplicationService(String serviceKey) {
		Object service = null;
		if (applicationServices == null) loadServices();
		service = applicationServices.get(serviceKey);
		return service;
	}

	public static Object getApplicationDao(String daoKey) {
		Object dao = null;
		if (applicationDaos == null) loadDaos();
		dao = applicationDaos.get(daoKey);
		return dao;
	}
	
	@SuppressWarnings("unchecked")
	private static void loadDaos() {
		applicationDaos = (Map<String, Object>) getBean(APPLICATION_DAOS_ID);
	}

	@SuppressWarnings("unchecked")
	private static void loadServices() {
		applicationServices = (Map<String, Object>) getBean(APPLICATION_SERVICES_ID);
	}
	
	public static Object getBean(String beanId) {
		return WebApplicationContextProvider.getApplicationContext().getBean(beanId);
	}
	
	public static String getHashedString(String theString, String usuario) throws WebException {
		String hashedString = StringUtils.EMPTY;
		try {
			usuario = StringUtils.upperCase(usuario);
			ShaPasswordEncoder shaPasswordEncoder = (ShaPasswordEncoder) getBean(PASSWORD_ENCODER_ID);
			hashedString = shaPasswordEncoder.encodePassword(theString, usuario);
		}
		catch (Exception e) {
//			logger.error("Error encriptando cadena", e);
			throw new WebException("Error encriptando cadena");
		}
		return hashedString;
	}
	
	public static String getDocumentStructures(String queryName) {
		String query = StringUtils.EMPTY;
		if (webDocumentsStructres == null){
			webDocumentsStructres = new ArrayList<XmlMarshalledObjectQuery>();
			webDocumentsStructres = loadQueries(DOCUMENTS_LOCATION, webDocumentsStructres);
		}
		XmlMarshalledObjectQuery objectToFind = new XmlMarshalledObjectQuery();
		objectToFind.setName(queryName);
		int index = Collections.binarySearch(webDocumentsStructres, objectToFind);
		if (index >= 0) query = webDocumentsStructres.get(index).getValue();
		return query;
	}
	
	public static String getQueryByName(String queryName) {
		String query = StringUtils.EMPTY;
		if (webQueries == null){
			webQueries = new ArrayList<XmlMarshalledObjectQuery>();
			loadQueries(DAO_QUERIES_LOCATION, webQueries);
		}
		XmlMarshalledObjectQuery objectToFind = new XmlMarshalledObjectQuery();
		objectToFind.setName(queryName);
		int index = Collections.binarySearch(webQueries, objectToFind);
		if (index >= 0) query = webQueries.get(index).getValue();
		return query;
	}
	
	private static List<XmlMarshalledObjectQuery> loadQueries(String location, List<XmlMarshalledObjectQuery> objects) {
		XmlMarshalledObject object = null;
		List<String> xmlQueryFiles = getXmlFiles(location);
		String xmlString = StringUtils.EMPTY;
		for (String queryFile : xmlQueryFiles) {
			xmlString = getXmlContent(location + "/" + queryFile);
			object = unmarshallXml(xmlString);
			objects.addAll(object.getQuery());
		}
		Collections.sort(objects);
		return objects;
	}
	
	private static List<String> getXmlFiles(String xmlPathLocation) {
		List<String> files = new ArrayList<String>();
		URL url = null;
		try {
			url = Thread.currentThread().getContextClassLoader().getResource(xmlPathLocation);
			File folder = new File(URLDecoder.decode(url.getPath(), "UTF-8"));
			File[] listOfFiles = folder.listFiles();
			for (File file : listOfFiles) if (file.isFile()) files.add(file.getName());
		}
		catch (Exception e) {
//			logger.error("Error leyendo directorio: " + e.getMessage(), e);
		}
		return files;
	}
	
	private static String getXmlContent(String xmlFileName) {
		String xmlString = StringUtils.EMPTY;
		InputStream inputStream = null;
		try {
			inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(xmlFileName);
			xmlString = IOUtils.toString(inputStream, DEFAULT_WEB_ENCODING);
		}
		catch (Exception e) {
//			logger.error("Error leyendo archivo XML: " + e.getMessage(), e);
		}
		return xmlString;
	}
	
	private static XmlMarshalledObject unmarshallXml(String xmlString) {
		XmlMarshalledObject output = new XmlMarshalledObject();
		JAXBContext context = null;
		try {
			context = JAXBContext.newInstance(XmlMarshalledObject.class);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			output = (XmlMarshalledObject) unmarshaller.unmarshal(new StringReader(xmlString));
		}
		catch (JAXBException e) {
//			logger.error("Error convirtiendo objeto a XML: " + e.getMessage(), e);
		}
		return output;
	}
	
	public static boolean isEmptyList(List<?> theList) {
		return theList == null || theList.size() == 0;
	}
	
	public static CustomUser getCustomUserFromAcegi() {
		Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		if (obj instanceof CustomUser) return (CustomUser) obj;
		else return null;
	}
	
	public static SgdUsuario getSgdUsuarioFromAcegi() throws WebException {
		Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		if (obj instanceof CustomUser){
			try {
				return (SgdUsuario) DBContext.getSgdUsuarioDao().findById(SgdUsuario.class, ((CustomUser) obj).getSgdUsuarioId());
			} catch (WebException e) {
				throw e;
			} catch (Exception e) {
//				logger.error(e);
				throw new WebException("Problemas a tratar de obtener el usuario");
			}
		}
		throw new WebException("Problemas a tratar de obtener el usuario");
	}
	
	public static Long getSgdUsuarioIdFromAcegi() {
		Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		if (obj instanceof CustomUser){
				return ((CustomUser) obj).getSgdUsuarioId();
		}
		return null;
	}
	
	public static List<?> sublist(List<?> list, int fromIndex, int numberOfRegisters){
		if(isEmptyList(list)) return list;
		int toIndex = fromIndex + numberOfRegisters;
		if(toIndex > list.size()) toIndex = list.size();
		return list.subList(fromIndex, toIndex);
	}
	
	public static String trimToZero(String value){
		value = StringUtils.trimToEmpty(value);
		return Pattern.matches("[-]*[\\d|\\.]*", value) && StringUtils.isNotBlank(value) ? value : "0";
	}
	
	public static String getDateDD_MM_YYYY_From_YYYYMMDD(String dateYYYYMMDD){
		try{
			return dateFormatAsDD_MM_YYYY.format(dateFormatAsYYYYMMDD.parse(dateYYYYMMDD));
		}catch(Exception e){
//			logger.error("Exception:", e);
		}
		return "";
	}
	
	public static String getAsCurrencie(String value){
		try{
			return decimalFormat2Decimals.format(new Double(value));
		}catch(Exception e){
			logger.error("Exception", e);
		}
		return "0.00";
	}

	public static String formatPhone(String value){
		return StringUtils.substring(value, 0, 4) + "-" + StringUtils.substring(value, 4, 8);
	}
	
	public static String formatDui(String dui){
		return StringUtils.substring(dui, 0, 8) + "-" + StringUtils.substring(dui, 8, 9);
	}
	
	public static String formatNit(String nit){
		return StringUtils.substring(nit, 0, 4) + "-" + StringUtils.substring(nit, 4, 10) + "-" + StringUtils.substring(nit, 10, 13) + "-" + StringUtils.substring(nit, 13, 14);
	}
	
	public static String getExceptionMessage(Exception e){
		return StringUtils.isNotBlank(e.getMessage()) ? e.getMessage() : DEFAULT_ERROR_MESSAGE;
	}

}
